package org.new9uy.tetris.game.type;

import org.new9uy.tetris.game.player.ai.Bot;
import org.new9uy.tetris.game.player.Player;
import java.awt.Color;

import org.new9uy.tetris.GameConstants;
import org.new9uy.tetris.controller.GUIController;
import org.new9uy.tetris.game.player.Human;
import org.new9uy.tetris.model.Matrix;
import org.new9uy.tetris.model.Next;
import org.new9uy.tetris.model.Score;

public class Game 
    implements Runnable
{
    private Player player;
    private Thread T;
    private int globalGravLevel = 0;
    
    //boolean used to keep the game loop running.
    private boolean playing = true;
    private static Game current;
    
    public static final Matrix matrix = new Matrix(GameConstants.MATRIX_X, GameConstants.MATRIX_Y);
    public static final Next next = new Next(matrix);
    public static final Score score = new Score(matrix);

    public Game(Human player) throws NullPointerException
    {
        if(player == null)
        {
            throw new NullPointerException("player cant be null");
        }

        setCurrent();
        
        this.player = player;
        
        matrix.setActive(true);
        player.getScore().reset();
        player.getMatrix().reset();
        player.getNext().getNext();//Erase next from previous game//////////////////

        matrix.spawnTetrimino(next.getNext());
    }

    private void setCurrent()
    {
        current = this;
    }
    
    public Player getPlayer()
    {
        return player;
    }

    public static Game current()
    {
        return current;
    }

    public boolean isPlayerKO()
    {
        return player.getMatrix().isOver();
    }
    //team = ko ssi tous les players sont ko

    private boolean isPlayerBot()
    {
        return (player.isBot());
    }

    public void koAnimation()
    {
        if (isPlayerKO())
        {
            matrix.spellSkullImage(Color.WHITE, Color.RED, Color.GRAY, 2);
            matrix.spellKO(Color.BLACK, 13);
        }
    }

    public void startGravities()
    {
        player.getGravity().pause(false);
        player.getGravity().start();
    }

    private void stopGravities()
    {
        player.getGravity().cancel();
    }

    public void pauseAllGravities(boolean pause)
    {
        player.getGravity().pause(pause);
    }

    public void startBots()
    {
        if (isPlayerBot())
        {
            Bot bot = (Bot)player;
            Thread thread = new Thread(bot);
            thread.setName("Bot");
            thread.start();
        }
    }

    private void stopBots()
    {
        if (isPlayerBot())
        {
            ((Bot) player).stop();
        }
    }

    public synchronized void startGame()
    {
        //Start everything
        startBots();
        T = new Thread(this);
        T.start();
        startGravities();
    }

    //player is the winning/losing players
    public void endGame()
    {
        //GUIController.current.returnToWelcome();
    }
    
    //if the game ends without finishing (e.g. DC)

    public void quitGame()
    {
        killThreads();
        T.interrupt();
    }

    //Must return null if no-one has won/lost yet.
    //If the return value is not null, then the game is over (see run()).
    public boolean checkFinish()
    {
        checkLvlUpAll();
        if (matrix.isOver())
        {
            return true;
        } 
        else
        {
            return false;
        }
    }

    private synchronized void killThreads()
    {
        stopBots();
        stopGravities();
    }
    //Game loop

    @Override
    public void run()
    {
        while (playing)
        {
            if (checkFinish())
            {
                playing = false;
                killThreads();
                try
                {
                    Thread.sleep(2000);
                } catch (InterruptedException e)
                {
                }
                endGame();
            }

            try
            {
                Thread.sleep(100);
            } catch (InterruptedException e)
            {
            }
        }
    }

    public boolean isPlaying()
    {
        return playing;
    }

    protected void lvlUpAll()
    {
        player.getGravity().lvlUp();
    }

    //+1 toutes les Gravity
    protected void checkLvlUpAll()
    {
        int lines = sumLines();

        //formula: Lvl up every 10 lines * num of players.
        if (lines / 10 > globalGravLevel)
        {
            lvlUpAll();
        }
    }
    
    //chaque player se met au level:
    //  (nb lignes qu'il a fait) / (nb de lignes totales)
    protected void lvlUpAsPercent()
    {
        int sum = sumLines();
        player.getGravity().setLevel(
                (sum - player.getScore().totalLinesDone()) / sum);
    }

    public int sumLines()
    {
        return player.getScore().totalLinesDone();
    }
}
